﻿using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using System.IO;

#if !XBOX360
using System.Runtime.Serialization.Formatters.Binary;
#endif
using System.Runtime.Serialization;
using Microsoft.Xna.Framework.Storage;

namespace PenguinForce.Levels
{
    [Serializable]
    public class Grid
    {
        public Grid(Texture2D nullTexture, SpriteSheet textureList)
        {
            if (nullTexture != null)
            {
                m_nullTexture = nullTexture;
            }
            if (textureList != null)
            {
                m_textureList = textureList;
            }
            m_grid = new Tile[WIDTH, HEIGHT];
            for (int i = 0; i < WIDTH; i++)
            {
                for (int j = 0; j < HEIGHT; j++)
                {
                    m_grid[i, j] = new Tile(textureList);
                }
            }
            m_startLocation = new Vector2(-1, -1);
            m_finishLocation = new Vector2(-1, -1);
            m_bearStartList = new List<Vector2>();
            m_parPenguins = 0;
            m_startingPenguins = 0;
            this.IsDesignMode = false;
        }

#if !XBOX360
        [NonSerialized]
#endif
        public bool IsDesignMode;

#if !XBOX360
        [OnDeserializedAttribute()]
        private void RunThisMethod(StreamingContext context)
        {
            fadingLayers = new List<FadingLayer>();
        }
#endif

        public void AddFadingLayer(FadingLayer layer)
        {
            fadingLayers.Add(layer);
        }

        public void PlaceStartFlag(int x, int y)
        {
            m_startLocation.X = x * GRID_SIZE + GRID_SIZE / 2;
            m_startLocation.Y = y * GRID_SIZE + GRID_SIZE / 2;
        }

        public void PlaceFinishFlag(int x, int y)
        {
            m_finishLocation.X = x * GRID_SIZE + GRID_SIZE / 2;
            m_finishLocation.Y = y * GRID_SIZE + GRID_SIZE / 2;
        }

        public void PlaceBear(int x, int y)
        {
            Vector2 newBear = new Vector2(x * GRID_SIZE + GRID_SIZE / 2, y * GRID_SIZE + GRID_SIZE / 2);
            if (!m_bearStartList.Contains(newBear))
            {
                m_bearStartList.Add(newBear);
            }
        }

        public float GetFriction(Vector2 position)
        {

            int gridX = (int)position.X / 32;
            int gridY = (int)position.Y / 32;

            foreach (FadingLayer fading in fadingLayers)
            {
                foreach (Tile tile in fading.m_tiles)
                {
                    if (position.X >= tile.m_location.X && position.X < tile.m_location.X + 32
                        && position.Y >= tile.m_location.Y && position.Y < tile.m_location.Y + 32)
                    {
                        return tile.GetFriction();
                    }
                }
            }

            return m_grid[gridX, gridY].GetFriction();
        }

        public void ClearBears()
        {
            m_bearStartList = new List<Vector2>();
        }

        public void Update(GameTime gameTime)
        {

            foreach (FadingLayer fading in fadingLayers)
            {
                fading.Update(gameTime);
            }
            int i = 0;
            while (i < fadingLayers.Count)
            {
                if (fadingLayers[i].HasExpired())
                {
                    fadingLayers.Remove(fadingLayers[i]);
                }
                else
                {
                    i++;
                }
            }
        }


        public void Draw(GameTime gameTime, SpriteBatch sprite, Vector2 scroll, Vector2 translationClamp)
        {
            int column = ((int)translationClamp.X / 32) - 20;
            int row = ((int)translationClamp.Y / 32) - 12;
            if (column < 0)
            {
                column = 0;
            }
            if (row < 0)
            {
                row = 0;
            }

            DrawGrid(sprite, scroll);
            if (IsDesignMode)
            {
                for (int i = column; i < column + 40; i++) // (int i = 0; i < WIDTH; i++)
                {
                    for (int j = row; j < row + 24; j++) // (int j = 0; j < HEIGHT; j++)
                    {
                        //this.Draw(gameTime, sprite, scroll, i, j, m_rockTextureList);
                    }
                }
            }
            else
            {
                for (int i = column; i < column + 40; i++) // (int i = 0; i < WIDTH; i++)
                {
                    for (int j = row; j < row + 24; j++) // (int j = 0; j < HEIGHT; j++)
                    {
                        this.DrawBlurEdges(gameTime, sprite, scroll, i, j);
                    }
                }
            }

            foreach (FadingLayer fading in fadingLayers)
            {
                fading.Draw(sprite, gameTime);
            }
            if (IsDesignMode)
            {
                DrawBears(sprite, scroll);
                DrawFlags(sprite, scroll);
            }
        }

        private void DrawBlurEdges(GameTime gameTime, SpriteBatch sprite, Vector2 scroll, int i, int j)
        {

            float x = i * 32 - (int)scroll.X / 2;
            float y = j * 32 - (int)scroll.Y / 2;

            if (i == 0 || j == 0 || i == this.Width || j == this.Height)
            {
                m_grid[i, j].Draw(gameTime, sprite, x, y);
                return;
            }

            if (m_grid[i, j].m_tileType == Tile.TileTypes.Rock)
            {
                int rockontop = (m_grid[i, j - 1].m_tileType == Tile.TileTypes.Rock) ? 1 : 0;
                int rockonleft = (m_grid[i - 1, j].m_tileType == Tile.TileTypes.Rock) ? 1 : 0;
                int rockonright = (m_grid[i + 1, j].m_tileType == Tile.TileTypes.Rock) ? 1 : 0;
                int rockonbottom = (m_grid[i, j + 1].m_tileType == Tile.TileTypes.Rock) ? 1 : 0;
                int bruteForce = 1 * rockontop + 2 * rockonleft + 4 * rockonright + 8 * rockonbottom;

                switch (bruteForce)
                {
                    case 0:
                        // top - 0 left - 0 right - 0 bottom - 0
                        m_grid[i, j].DrawBlendTexture(gameTime, sprite, x, y, Tile.RockBlend.RockLone, m_textureList);
                        break;
                    case 1:
                        // top - 1 left - 0 right - 0 bottom - 0
                        m_grid[i, j].DrawBlendTexture(gameTime, sprite, x, y, Tile.RockBlend.RockN, m_textureList);
                        break;
                    case 2:
                        // top - 0 left - 1 right - 0 bottom - 0
                        m_grid[i, j].DrawBlendTexture(gameTime, sprite, x, y, Tile.RockBlend.RockW, m_textureList);
                        break;
                    case 3:
                        // top - 1 left - 1 right - 0 bottom - 0
                        m_grid[i, j].DrawBlendTexture(gameTime, sprite, x, y, Tile.RockBlend.RockNW, m_textureList);
                        break;
                    case 4:
                        // top - 0 left - 0 right - 1 bottom - 0
                        m_grid[i, j].DrawBlendTexture(gameTime, sprite, x, y, Tile.RockBlend.RockE, m_textureList);
                        break;
                    case 5:
                        // top - 1 left - 0 right - 1 bottom - 0
                        m_grid[i, j].DrawBlendTexture(gameTime, sprite, x, y, Tile.RockBlend.RockNE, m_textureList);
                        break;
                    case 6:
                        // top - 0 left - 1 right - 1 bottom - 0
                        m_grid[i, j].DrawBlendTexture(gameTime, sprite, x, y, Tile.RockBlend.RockWE, m_textureList);
                        break;
                    case 7:
                        // top - 1 left - 1 right - 1 bottom - 0
                        m_grid[i, j].DrawBlendTexture(gameTime, sprite, x, y, Tile.RockBlend.RockWNE, m_textureList);
                        break;
                    case 8:
                        // top - 0 left - 0 right - 0 bottom - 1
                        m_grid[i, j].DrawBlendTexture(gameTime, sprite, x, y, Tile.RockBlend.RockS, m_textureList);
                        break;
                    case 9:
                        // top - 1 left - 0 right - 0 bottom - 1
                        m_grid[i, j].DrawBlendTexture(gameTime, sprite, x, y, Tile.RockBlend.RockNS, m_textureList);
                        break;
                    case 10:
                        // top - 0 left - 1 right - 0 bottom - 1
                        m_grid[i, j].DrawBlendTexture(gameTime, sprite, x, y, Tile.RockBlend.RockSW, m_textureList);
                        break;
                    case 11:
                        // top - 1 left - 1 right - 0 bottom - 1
                        m_grid[i, j].DrawBlendTexture(gameTime, sprite, x, y, Tile.RockBlend.RockSWN, m_textureList);
                        break;
                    case 12:
                        // top - 0 left - 0 right - 1 bottom - 1
                        m_grid[i, j].DrawBlendTexture(gameTime, sprite, x, y, Tile.RockBlend.RockSE, m_textureList);
                        break;
                    case 13:
                        // top - 1 left - 0 right - 1 bottom - 1
                        m_grid[i, j].DrawBlendTexture(gameTime, sprite, x, y, Tile.RockBlend.RockNES, m_textureList);
                        break;
                    case 14:
                        // top - 0 left - 1 right - 1 bottom - 1
                        m_grid[i, j].DrawBlendTexture(gameTime, sprite, x, y, Tile.RockBlend.RockESW, m_textureList);
                        break;
                    case 15:
                        // top - 1 left - 1 right - 1 bottom - 1
                        m_grid[i, j].DrawBlendTexture(gameTime, sprite, x, y, Tile.RockBlend.Rock, m_textureList);
                        break;
                    default:
                        break;
                }
            }
            else
            {
                m_grid[i, j].Draw(gameTime, sprite, x, y);
            }
        }

        private void DrawFlags(SpriteBatch sprite, Vector2 scroll)
        {
            if (m_startLocation.X != -1 && m_startLocation.Y != -1)
            {
                sprite.Draw(m_startFlagTexture, new Vector2(m_startLocation.X - (int)scroll.X / 2, m_startLocation.Y - (int)scroll.Y / 2), new Rectangle(0, 0, 128, 128), Color.White, 0.0f, new Vector2(64, 128), 1.0f, SpriteEffects.None, 1.0f);
            }
            if (m_finishLocation.X != -1 && m_finishLocation.Y != -1)
            {
                sprite.Draw(m_finishFlagTexture, new Vector2(m_finishLocation.X - (int)scroll.X / 2, m_finishLocation.Y - (int)scroll.Y / 2), new Rectangle(0, 0, 128, 128), Color.White, 0.0f, new Vector2(64, 128), 1.0f, SpriteEffects.None, 1.0f);
            }
        }

        private void DrawBears(SpriteBatch sprite, Vector2 scroll)
        {
            foreach (Vector2 position in m_bearStartList)
            {
                sprite.Draw(m_bearStartTexture, new Vector2(position.X - (int)scroll.X / 2, position.Y - (int)scroll.Y / 2), new Rectangle(0, 0, 64, 64), Color.White, 0.0f, new Vector2(32, 64), 1.0f, SpriteEffects.None, 1.0f);
            }
        }

        private void DrawGrid(SpriteBatch sprite, Vector2 scroll)
        {
            for (int y = 0; y < HEIGHT; y++)
            {
                for (int x = 0; x < WIDTH; x++)
                {

                    Rectangle dest = new Rectangle();
                    dest.X = (x * 32) - (int)scroll.X / 2;
                    dest.Y = (y * 32) - (int)scroll.Y / 2;
                    dest.Width = 32;
                    dest.Height = 32;

                    if (x < WIDTH - 1)
                    {
                        sprite.Draw(m_nullTexture, new Rectangle(dest.X, dest.Y, 32, 1), new Color(255, 0, 0, 100));
                    }
                    if (y < HEIGHT - 1)
                    {

                        sprite.Draw(m_nullTexture, new Rectangle(dest.X, dest.Y, 1, 32), new Color(255, 0, 0, 100));
                    }

                    if (x < WIDTH - 1 && y < HEIGHT - 1)
                    {
                        sprite.Draw(m_nullTexture, dest, new Color(255, 0, 0, 100));

                    }

                }
            }
        }

#if !XBOX360
        public void Save(string path)
        {
            //using (Stream saveFile = File.OpenWrite(@"data/" + path + ".pgn"))
            using (Stream saveFile = File.OpenWrite(Path.Combine(StorageContainer.TitleLocation, "Content/" + string.Format("Levels/{0}.pgn", path))))
            {
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(saveFile, this);
                saveFile.Close();
            }
            //System.IO.BinaryWriter writer = new System.IO.BinaryWriter(File.Open(@"data/" + path + ".pgn", FileMode.Create));

            //writer.Write(GRID_SIZE);
            //writer.Write(Width);
            //writer.Write(Height);
            //writer.Write(m_startLocation.X);
            //writer.Write(m_startLocation.Y);
            //writer.Write(m_finishLocation.X);
            //writer.Write(m_finishLocation.Y);

            //writer.Write(m_bearStartList.Count);
            //foreach (Vector2 bearPosition in m_bearStartList)
            //{
            //    writer.Write(bearPosition.X);
            //    writer.Write(bearPosition.Y);
            //}

            //for (int i = 0; i < WIDTH; i++)
            //{
            //    for (int j = 0; j < HEIGHT; j++)
            //    {
            //        writer.Write((int)m_grid[i, j].m_tileType);

            //    }
            //}

            //writer.Close();
        }
#endif

        public static Grid Load(string path)
        {
            //using (Stream loadFile = File.OpenRead(@"data/" + path + ".pgn"))
            using (FileStream loadFile = File.OpenRead(Path.Combine(StorageContainer.TitleLocation, "Content/" + string.Format("Levels/{0}.pgn", path))))
            {
                BinaryReader reader = new BinaryReader(loadFile);

                Grid grid = new Grid(null, null);

                int numBears = reader.ReadInt32();
                grid.m_bearStartList = new List<Vector2>();
                for (int index = 0; index < numBears; index++)
                {
                    grid.m_bearStartList.Add(
                        new Vector2(
                            reader.ReadSingle(),
                            reader.ReadSingle()
                        )
                    );
                }

                grid.m_finishLocation = new Vector2(
                    reader.ReadSingle(),
                    reader.ReadSingle()
                );

                grid.m_introText = reader.ReadString();

                grid.m_parPenguins = reader.ReadInt32();

                grid.m_startingPenguins = reader.ReadInt32();

                grid.m_startLocation = new Vector2(
                    reader.ReadSingle(),
                    reader.ReadSingle()
                );

                for (int row = 0; row < grid.Height; row++)
                {
                    for (int col = 0; col < grid.Width; col++)
                    {
                        grid.m_grid[col, row].m_location = new Vector2(
                            reader.ReadSingle(),
                            reader.ReadSingle()
                        );
                        grid.m_grid[col, row].m_tileType = (Tile.TileTypes)reader.ReadInt32();
                    }
                }

                reader.Close();

                return grid;
            }
        }

#if !XBOX360
        public static Grid Load(FileStream loadFile)
        {
            BinaryFormatter formatter = new BinaryFormatter();
            Grid grid = formatter.Deserialize(loadFile) as Grid;
            loadFile.Close();
            return grid;
        }
#endif

        //public void Load(String path)
        //{
        //    System.IO.BinaryReader reader = new System.IO.BinaryReader(File.Open(@"data/" + path + ".pgn", FileMode.Open));
        //    m_bearStartList = new List<Vector2>();

        //    reader.ReadInt32();
        //    reader.ReadInt32();
        //    reader.ReadInt32();

        //    m_startLocation = new Vector2(reader.ReadSingle(), reader.ReadSingle());
        //    m_finishLocation = new Vector2(reader.ReadSingle(), reader.ReadSingle());

        //    int count = reader.ReadInt32();

        //    for (int i = 0; i < count; i++)
        //    {
        //        m_bearStartList.Add(new Vector2(reader.ReadSingle(), reader.ReadSingle()));
        //    }

        //    for (int i = 0; i < WIDTH; i++)
        //    {
        //        for (int j = 0; j < HEIGHT; j++)
        //        {
        //            m_grid[i, j].m_tileType = (Tile.TileTypes)reader.ReadInt32();

        //        }
        //    }

        //    reader.Close();
        //}

        public bool OnGrid(int x, int y)
        {
            if (x >= 0 && x < WIDTH && y >= 0 && y < HEIGHT)
            {
                return true;
            }
            return false;
        }

        public int Width
        {
            get
            {
                return WIDTH;
            }
        }

        public int Height
        {
            get
            {
                return HEIGHT;
            }
        }

        public int GRIDSIZE
        {
            get
            {
                return GRID_SIZE;
            }

        }

        public const int GRID_SIZE = 32;
        public const int WIDTH = 200;
        public const int HEIGHT = 200;

        public Tile[,] m_grid;

        public Vector2 m_startLocation;
        public Vector2 m_finishLocation;

        public List<Vector2> m_bearStartList;

#if !XBOX360
        [NonSerialized]
#endif
        private static Texture2D m_nullTexture;

#if !XBOX360
        [NonSerialized]
#endif
        public static Texture2D m_startFlagTexture;

#if !XBOX360
        [NonSerialized]
#endif
        public static Texture2D m_finishFlagTexture;

#if !XBOX360
        [NonSerialized]
#endif
        public static Texture2D m_bearStartTexture;

#if !XBOX360
        [NonSerialized]
#endif
        public static SpriteSheet m_textureList;

#if !XBOX360
        [NonSerialized]
#endif
        public static List<Texture2D> rockTextures = new List<Texture2D>();

#if !XBOX360
        [NonSerialized]
#endif
        private List<FadingLayer> fadingLayers = new List<FadingLayer>();

        public int m_startingPenguins;
        public int m_parPenguins;
        public string m_introText;

        //public static List<Texture2D> m_rockTextureList;
    }
}